package com.dynamic.mybatis.core.parser.field;

import cn.hutool.core.date.DatePattern;
import com.dynamic.mybatis.core.datasource.DataSourceAttributesKey;
import com.dynamic.mybatis.core.enums.Type;
import com.dynamic.mybatis.core.enums.TypeAlias;
import com.dynamic.mybatis.core.metadata.MappedStatementFieldValidator;
import com.dynamic.mybatis.core.metadata.MappedStatementMetaField;
import com.dynamic.mybatis.core.metadata.MappedStatementTable;
import com.dynamic.mybatis.core.session.DynamicSqlSession;
import com.dynamic.mybatis.core.toolkit.StringUtils;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.JavaTypeResolver;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.internal.db.ActualTableName;
import org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl;
import org.mybatis.generator.internal.util.JavaBeansUtil;

import java.math.BigDecimal;
import java.sql.*;
import java.util.*;

/**
 * @Description
 * @Author xs
 * @Date 2023/4/12 10:25
 */
public class DatabaseMetaDataHelper {
    private BigDecimal one = new BigDecimal(1);
    private BigDecimal two = new BigDecimal(2);

    private String primaryKey_key = "primaryKey";

    public List<MappedStatementTable> getDatabaseMeta(DynamicSqlSession dynamicSqlSession,String dataSourceKey, String... tableNames){
      List<MappedStatementTable> list = new ArrayList<>();
      for(String tableName : tableNames){
          try {
              list.add(getTransportDatabaseMeta(getDatabaseMeta(dynamicSqlSession,dataSourceKey,tableName),dynamicSqlSession,dataSourceKey));
          }catch (Exception e){
              throw  new  RuntimeException(e);
          }
      }
      return list;
    }




    public MappedStatementTable getTransportDatabaseMeta(DatabaseTable databaseTable,DynamicSqlSession dynamicSqlSession,String dataSourceKey){
        MappedStatementTable table = new MappedStatementTable();
        table.setTableName(databaseTable.getTableName());

        List<MappedStatementMetaField> fields = new ArrayList<>();

        Map<Object,Object> attrs = dynamicSqlSession.getDataSourceAttributes(dataSourceKey);
        attrs = attrs != null ? attrs : Collections.EMPTY_MAP  ;

        for(int i = 0 ; i < databaseTable.getColumns().size() ; i++){
            IntrospectedColumn column = databaseTable.getColumns().get(i);
            MappedStatementMetaField field  = new MappedStatementMetaField();
            field.setColumn(column.getActualColumnName());
            field.setField(StringUtils.underlineToCamel(column.getActualColumnName()));
            field.setVisible(Boolean.TRUE);
            field.setName(column.getRemarks());
            Class jdbcTypeClass = TypeAlias.getType(column.getFullyQualifiedJavaType().getFullyQualifiedName());
            field.setJavaTypeClass(jdbcTypeClass);
            field.setJavaType(jdbcTypeClass.getName());
            JDBCType jdbcType = JDBCType.valueOf(column.getJdbcType());
            Type type = Type.getType(jdbcType);
            field.setType(type.getType());
            field.setJdbcType(jdbcType);
            field.setAutoincrement(column.isAutoIncrement());
            field.setPrimaryKey("1".equals(column.getProperties().getProperty(primaryKey_key)));

            field.setLogicDelete(DataSourceAttributesKey.hasLogicDelete(field.getColumn(),attrs));
            field.setLogicDeleteValue(DataSourceAttributesKey.getLogicDeleteValue(attrs));
            field.setLogicNotDeleteValue(DataSourceAttributesKey.getLogicNotDeleteValue(attrs));
            field.setCreateBy(DataSourceAttributesKey.hasCreateBy(field.getColumn(),attrs));
            field.setCreateTime(DataSourceAttributesKey.hasCreateTime(field.getColumn(),attrs));
            field.setUpdateBy(DataSourceAttributesKey.hasUpdateBy(field.getColumn(),attrs));
            field.setUpdateTime(DataSourceAttributesKey.hasUpdateTime(field.getColumn(),attrs));

            MappedStatementFieldValidator validator = new MappedStatementFieldValidator();
            validator.setRequired(!column.isNullable());

            //默认为无符号
            if(type.getMin() != null && type.getMin() != null){
                validator.setUnsigned(true);
                validator.setMin(0);
                validator.setMax(new BigDecimal(type.getMax().toString()).multiply(two).add(one));
            }
            if(Type.StandardType.VARCHAR.type.equals(field.getType())){
                validator.setMaxLength(column.getLength());
            }else {
                validator.setMaxLength(type.getLength());
            }
            if(field.getJdbcType() == JDBCType.TIMESTAMP){
                validator.setDatePattern(DatePattern.NORM_DATETIME_PATTERN);
            }else if(field.getJdbcType() == JDBCType.DATE){
                validator.setDatePattern(DatePattern.NORM_DATE_PATTERN);
            }else if(field.getJdbcType() == JDBCType.TIME){
                validator.setDatePattern(DatePattern.NORM_TIME_PATTERN);
            }
            field.setValidator(validator);
            field.setSort((byte)i);
            fields.add(field);
        }

        table.setFields(fields);
        return table;
    }

    public Set<String> getTables(Connection connection) throws SQLException {
        DatabaseMetaData databaseMetaData = connection.getMetaData();
        String localCatalog = connection.getCatalog();
        String localSchema = connection.getSchema();
        ResultSet rs = databaseMetaData.getTables(localCatalog,localSchema,"%",new String[]{"TABLE"});
        Set<String> set = new HashSet<>();
        while (rs.next()) {
            set.add(rs.getString("TABLE_NAME "));
        }
        return set;
    }


    public  DatabaseTable getDatabaseMeta(DynamicSqlSession dynamicSqlSession,String dataSourceKey, String tableName) throws SQLException {
        Connection connection =  dynamicSqlSession.getConnectionByDataSourceKey(dataSourceKey);

        DatabaseMetaData databaseMetaData = connection.getMetaData();
        String localCatalog = connection.getCatalog();
        String localSchema = connection.getSchema();
        String localTableName = tableName;

        ResultSet resultSet =  databaseMetaData.getPrimaryKeys(localCatalog,localSchema,localTableName);
        Set<String> primaryKeyColumns = new HashSet<>();
        while(resultSet.next()) {
            primaryKeyColumns.add(resultSet.getString("COLUMN_NAME"));
        }
        resultSet.close();

        List<IntrospectedColumn> columns = new ArrayList<>();
        ResultSet rs = databaseMetaData.getColumns(localCatalog, localSchema,localTableName, "%");

        JavaTypeResolver javaTypeResolver = new  JavaTypeResolverDefaultImpl();

        ActualTableName atn = null;
        while (rs.next()) {
            IntrospectedColumn introspectedColumn = new IntrospectedColumn();

            introspectedColumn.setJdbcType(rs.getInt("DATA_TYPE")); //$NON-NLS-1$
            introspectedColumn.setActualTypeName(rs.getString("TYPE_NAME")); //$NON-NLS-1$
            introspectedColumn.setLength(rs.getInt("COLUMN_SIZE")); //$NON-NLS-1$
            introspectedColumn.setActualColumnName(rs.getString("COLUMN_NAME")); //$NON-NLS-1$
            introspectedColumn.setNullable(rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable); //$NON-NLS-1$
            introspectedColumn.setScale(rs.getInt("DECIMAL_DIGITS")); //$NON-NLS-1$
            introspectedColumn.setRemarks(rs.getString("REMARKS")); //$NON-NLS-1$
            introspectedColumn.setDefaultValue(rs.getString("COLUMN_DEF")); //$NON-NLS-1$

            introspectedColumn.getProperties().setProperty(primaryKey_key,primaryKeyColumns.contains(introspectedColumn.getActualColumnName())?"1":"0");

            introspectedColumn.setAutoIncrement("YES".equals(rs.getString("IS_AUTOINCREMENT"))); //$NON-NLS-1$ //$NON-NLS-2$

            introspectedColumn.setGeneratedColumn("YES".equals(rs.getString("IS_GENERATEDCOLUMN"))); //$NON-NLS-1$ //$NON-NLS-2$

            introspectedColumn.setJavaProperty(JavaBeansUtil.getCamelCaseString(introspectedColumn.getActualColumnName(), false));

            FullyQualifiedJavaType fullyQualifiedJavaType = javaTypeResolver.calculateJavaType(introspectedColumn);
            if (fullyQualifiedJavaType != null) {
                introspectedColumn.setFullyQualifiedJavaType(fullyQualifiedJavaType);
                introspectedColumn.setJdbcTypeName(javaTypeResolver.calculateJdbcTypeName(introspectedColumn));
            }

            if(atn == null){
                atn = new ActualTableName(
                        rs.getString("TABLE_CAT"), //$NON-NLS-1$
                        rs.getString("TABLE_SCHEM"), //$NON-NLS-1$
                        rs.getString("TABLE_NAME")); //$NON-NLS-1$
            }
            columns.add(introspectedColumn);
        }
        rs.close();
        connection.close();
        return new DatabaseTable(localCatalog,localSchema,localTableName,columns);
    }

    public static class DatabaseTable extends ActualTableName {
        List<IntrospectedColumn> columns;
        String remarks;
        public DatabaseTable(String catalog, String schema, String tableName,List<IntrospectedColumn> columns) {
            super(catalog, schema, tableName);
            this.columns = columns;
        }

        public List<IntrospectedColumn> getColumns() {
            return columns;
        }

        public void setColumns(List<IntrospectedColumn> columns) {
            this.columns = columns;
        }

        public String getRemarks() {
            return remarks;
        }

        public void setRemarks(String remarks) {
            this.remarks = remarks;
        }
    }

}